package com.study.system.edusearchservice.dao;

import com.alibaba.fastjson.JSON;
import com.edu.common.entity.ElasticsearchEntity;
import com.edu.common.entity.PageEntity;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * ES操作类
 */
@Repository
public class ElasticsearchDAO {

    private static Logger logger = LoggerFactory.getLogger(ElasticsearchDAO.class);

    @Resource
    private RestHighLevelClient client;

    /**
     * 索引是否存在
     * @param indexName
     * @return
     */
    public boolean existsIndex(String indexName) throws IOException {
        GetIndexRequest request = new GetIndexRequest(indexName);
        boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
        logger.info("{}索引是否存在{}",indexName,exists);
        return exists;
    }

    /**
     * 删除索引
     * @param indexName
     */
    public void deleteIndex(String indexName) throws IOException {
        //创建删除索引的请求
        DeleteIndexRequest request = new DeleteIndexRequest(indexName);
        //删除索引
        AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
        logger.info("{}被删除",indexName);
    }

    /**
     * 创建索引
     * @param indexName
     */
    public void createIndex(String indexName) throws IOException {
        //创建索引的请求
        CreateIndexRequest request = new CreateIndexRequest(indexName);
        //创建索引
        CreateIndexResponse resp = client.indices().create(request, RequestOptions.DEFAULT);
        logger.info("{}创建完成",indexName);
    }

    /**
     * 批量插入
     * @param indexName
     * @param entities
     */
    public void insertBatch(String indexName, List<ElasticsearchEntity> entities) throws IOException {
        //创建批量请求
        BulkRequest request = new BulkRequest(indexName);
        //遍历数据集合
        entities.forEach(entity -> {
            //添加数据到请求中 id制定id，source制定JSON字符串
            request.add(new IndexRequest()
                    .id(String.valueOf(entity.getId()))
                    .source(JSON.toJSONString(entity.getData()), XContentType.JSON));
        });
        //执行批量插入
        client.bulk(request,RequestOptions.DEFAULT);
        logger.info("{}批量插入完成{}条数据",indexName,entities.size());
    }

    /**
     * 通用分页搜索方法
     * @param indexName 索引名称
     * @param from 开始行号
     * @param size 查询长度
     * @param map 查询条件集合
     * @param sort 排序方式集合
     * @param c 实体类型
     * @return
     * @throws IOException
     */
    public PageEntity search(String indexName, int from, int size,
                                             Map<String,String> map,Map<String,String> sort,
                             Class c,String... highlightFields) throws IOException {
        SearchRequest request = new SearchRequest(indexName);
        SearchSourceBuilder builder = new SearchSourceBuilder();
        //执行布尔查询，通过多个条件进行过滤
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if(map != null && map.size() > 0) {
            for (String key : map.keySet()) {
                String value = map.get(key);
                //如果条件内容不为空，按该条件进行过滤
                if(StringUtils.hasText(value)) {
                    boolQueryBuilder.filter(QueryBuilders.matchPhraseQuery(key, value));
                }
            }
        }
        //设置查询条件
        if(boolQueryBuilder.filter().size() > 0) {
            builder.query(boolQueryBuilder);
        }
        //执行排序字段和方式
        if(sort != null && sort.size() > 0){
            String field = sort.get("field");
            String type = sort.get("type");
            builder.sort(new FieldSortBuilder(field).order(SortOrder.fromString(type)));
        }
        builder.from(from);
        builder.size(size);
        //创建高亮生成器
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        //设置高亮字段
        for(String field : highlightFields){
            highlightBuilder.field(field);
        }
        //设置前后标签
        highlightBuilder.preTags("<span style='color:red'>").postTags("</span>");
        builder.highlighter(highlightBuilder);
        //指定分页位置和长度
        request.source(builder);
        //处理查询结果
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        SearchHit[] hits = response.getHits().getHits();
        List res = new ArrayList<>();
        for (SearchHit hit : hits) {
            Object obj = JSON.parseObject(hit.getSourceAsString(), c);
            res.add(obj);
            //获得所有高亮的字段
            Map<String, HighlightField> hFields = hit.getHighlightFields();
            for(String hField : hFields.keySet()){
                //获得反射字段
                try {
                    Field declaredField = c.getDeclaredField(hField);
                    //将字段的值替换为带高亮效果的属性值
                    declaredField.setAccessible(true);
                    Text[] fragments = hFields.get(hField).fragments();
                    if(declaredField != null && fragments.length > 0){
                        declaredField.set(obj,fragments[0].string());
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        //包装到分页实体类中
        PageEntity entity = new PageEntity();
        entity.setData(res);
        entity.setTotal((int) response.getHits().getTotalHits().value);
        entity.setPageSize(size);
        return entity;
    }

    public void saveOrUpdate(String indexName, ElasticsearchEntity entity) throws IOException {
        if(entity.getId() == null){
            logger.error("数据没有id");
            return;
        }
        //创建索引操作请求
        IndexRequest request = new IndexRequest(indexName);
        //设置一个文档数据
        request.id(entity.getId().toString()).source(JSON.toJSONString(entity.getData()),XContentType.JSON);
        //通过rest客户端发送更新或添加请求
        client.index(request, RequestOptions.DEFAULT);
        logger.info("文档保存成功 {}",entity);
    }

    public void deleteById(String indexName, String id) throws IOException {
        //创建删除文档请求
        DeleteByQueryRequest request = new DeleteByQueryRequest(indexName);
        //设置删除条件
        request.setQuery(QueryBuilders.termQuery("id",id));
        client.deleteByQuery(request, RequestOptions.DEFAULT);
        logger.info("文档删除成功 {}", id);
    }
}
